/**
 * Copyright (c) 2012 Nokia Corporation.
 */
package com.nokia.lib.iap;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.midlet.MIDlet;


import com.nokia.lib.iap.uicomponents.Buttons;
import com.nokia.lib.iap.uicomponents.ProgressBar;
import com.nokia.lib.iap.uicomponents.StepIndicator;
import com.nokia.lib.iap.uicomponents.TextWrapper;
import com.nokia.mid.payment.IAPClientPaymentException;
import com.nokia.mid.payment.IAPClientPaymentListener;
import com.nokia.mid.payment.IAPClientPaymentManager;
import com.nokia.mid.payment.IAPClientProductData;
import com.nokia.mid.payment.IAPClientUserAndDeviceData;

import com.nokia.mid.ui.VirtualKeyboard;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import org.json.me.JSONException;
import org.json.me.JSONObject;

/**
 * IapCanvas is responsible for displaying the UI and controlling the
 * application logic. All the IAP related code resides in this class.
 */
public class IapCanvas
        extends Canvas
        implements IAPClientPaymentListener,
        Buttons.Listener,
        ProgressBar.Listener,
        CommandListener {
    // Constants

    private static final int DEFAULT_BACKGROUND_COLOR = 0xff666666;
    private static final int DEFAULT_FOREGROUND_COLOR = 0xffffffff;
    private static final int MARGIN = 6;
    // STEPS
    private static final int START = 1;
    private static final int RETRIEVING_PRODUCT_INFORMATION = 2;
    private static final int SHOW_PRODUCT_INFORMATION = 3;
    private static final int SHOW_RESULT = 4;
    // IAP
    private static final String PRODUCT_ID = "588547";
    private static final String DRM_PATH = "drm/data/resourceid_";
    public static final String lockedFname = "data.txt";
    // Members
    private TrynBuyWrapperListener _midlet = null;
    private StepIndicator _stepIndicator = null;
    private Buttons _buttons = null;
    private ProgressBar _progressBar = null;
    private TextWrapper _textWrapper = null;
    private String _infoText = null;
    private int _currentStep = 1;
    private boolean _isKeypadON = true;
    // IAP
    private IAPClientPaymentManager _iapClientPaymentManager = null;
    // iap 
    private final BasicRMS rms = new BasicRMS();
    
    private boolean isHidden = false;
    private int triesLeft;

    /**
     * Constructor.
     *
     * @param midlet The app MIDlet.
     */
    public IapCanvas(TrynBuyWrapperListener midlet) {
        _midlet = midlet;
        
        try {
            _iapClientPaymentManager = IAPClientPaymentManager.getIAPClientPaymentManager();
        } catch (IAPClientPaymentException e){}
        IAPClientPaymentManager.setIAPClientPaymentListener(this);

        if(isBought()){
            _midlet.launchApp();
            isHidden = true;
            return;
        }
        
        final int width = getWidth();
        final int height = getHeight();

        try {
            Class.forName("com.nokia.mid.ui.VirtualKeyboard");
            VirtualKeyboard.hideOpenKeypadCommand(true);
            _isKeypadON = false;
            setFullScreenMode(true);
            _buttons = new Buttons(this, width, height);
        } catch (ClassNotFoundException e) {
            // enable keypad
            setCommandListener(this);
        }

        _stepIndicator = new StepIndicator(width, height, 4);
        _progressBar = new ProgressBar(width - MARGIN * 2, ProgressBar.HEIGHT, this);
        _progressBar.setParent(this);
        _progressBar.setPosition(MARGIN, height / 2);
        _textWrapper = new TextWrapper(Font.getDefaultFont(), width - MARGIN * 2);
        states = new State[]{
            new State(START, null, startCommand, exitCommand),
            new State(RETRIEVING_PRODUCT_INFORMATION, leftCancelCommand, null, exitCommand),
            new State(SHOW_PRODUCT_INFORMATION, leftCancelCommand, buyCommand, exitCommand),
            new State(SHOW_RESULT, null, doneCommand, exitCommand)
        };
        _iapClientPaymentManager.getUserAndDeviceId(IAPClientPaymentManager.DEFAULT_AUTHENTICATION);
    }

    /**
     * Methods (callbacks) from IAPClientPaymentListener.
     */
    public void productDataListReceived(int arg0, IAPClientProductData[] arg1) {
        // Not implemented		
    }

    public void productDataReceived(int status, IAPClientProductData productData) {
        if (_currentStep == START || _currentStep == SHOW_RESULT) {
            // Cancelled?
            return;
        }

        StringBuffer buffer = new StringBuffer();
        if(status == IAPClientPaymentListener.OK || status == IAPClientPaymentListener.RESTORABLE){
            buffer.append("Title: ").append(productData.getTitle()).append('\n');
            buffer.append("Short description: ").append(productData.getShortDescription()).append('\n');
            buffer.append("Long description: ").append(productData.getLongDescription()).append('\n');
            buffer.append("DRM protection: ").append(productData.getDrmProtection()).append('\n');
            buffer.append("Price: ").append(productData.getPrice());

            _progressBar.stop(100);
            _infoText = buffer.toString();

            setStep(SHOW_PRODUCT_INFORMATION);
        }else{
            buffer.append("Failed getting product info!\n");
            setStep(SHOW_RESULT);
        }
    }

    public void purchaseCompleted(int status, String ticket) {
        if (_currentStep == START || _currentStep == SHOW_RESULT) {
            // Cancelled?
            return;
        }

        boolean success = (status == IAPClientPaymentListener.OK
                || status == IAPClientPaymentListener.RESTORABLE);

        StringBuffer buffer = new StringBuffer();

        if (success) {
            buffer.append("Product purchased successfully!\n");
            serialiseProduct(PRODUCT_ID);
        } else {
            buffer.append("Failed to purchase the product!\n");
        }

        buffer.append("Status: ").append(status).append('\n');
        buffer.append("Ticket: ").append(ticket).append('\n');

        _infoText = buffer.toString();

        setStep(SHOW_RESULT);
    }

    public void restorableProductsReceived(int arg0, IAPClientProductData[] arg1) {
        // Not implemented
    }

    public void restorationCompleted(int status, String ticket) {
        if (_currentStep == START || _currentStep == SHOW_RESULT) {
            // Cancelled?
            return;
        }

        boolean success = (status == IAPClientPaymentListener.OK
                || status == IAPClientPaymentListener.RESTORABLE);

        StringBuffer buffer = new StringBuffer();

        if (success) {
            buffer.append("Product restored successfully!\n");
        } else {
            buffer.append("Failed to restore the product!\n");
        }

        buffer.append("Status: ").append(status).append('\n');
        buffer.append("Ticket: ").append(ticket).append('\n');

        _infoText = buffer.toString();

        setStep(SHOW_RESULT);
    }

    public void userAndDeviceDataReceived(int status, IAPClientUserAndDeviceData data) {
        if(status == IAPClientPaymentListener.OK && data != null){
            triesLeft = getProductTNBStatus(data);
        }
        setStep(START);
    }

    /**
     * From Canvas.
     */
    protected void paint(Graphics graphics) {
        if(isHidden)
            return;
        
        // Paint the background
        graphics.setColor(DEFAULT_BACKGROUND_COLOR);
        graphics.fillRect(0, 0, getWidth(), getHeight());

        // Paint the informational text
        graphics.setColor(DEFAULT_FOREGROUND_COLOR);

        if (_infoText != null && _infoText.length() > 0) {
            try {
                _textWrapper.setText(_infoText);
                int y = MARGIN * 2;

                while (_textWrapper.hasMoreLines()) {
                    graphics.drawString(_textWrapper.nextLine(),
                            MARGIN, y, Graphics.TOP | Graphics.LEFT);
                    y += _textWrapper.lineHeight();
                }
            } catch (IllegalArgumentException e) {
            }
        }

        // Paint the step indicator and buttons
        _stepIndicator.paint(graphics);
        if (_buttons != null) {
            _buttons.paint(graphics);
        }
    }

    /**
     * From Canvas.
     */
    protected void pointerPressed(int x, int y) {
        if (_buttons == null) {
            return;
        }

        _buttons.onPointerPressed(x, y);
        repaint();
    }

    protected void pointerReleased(int x, int y) {
        if (_buttons == null) {
            return;
        }

        _buttons.onPointerReleased(x, y);
        repaint();
    }

    protected void pointerDragged(int x, int y) {
        if (_buttons == null) {
            return;
        }

        _buttons.onPointerPressed(x, y);
        repaint();
    }

    /**
     * From Buttons.Listener.
     */
    public void onButtonClicked(int id) {
        switch (id) {
            case Buttons.POSITIVE_ACTION:
                switch (_currentStep) {
                    case START:
                        _midlet.launchApp();
                        break;

                    case SHOW_PRODUCT_INFORMATION:
                        // Buy
                        int errorCode = _iapClientPaymentManager.purchaseProduct(PRODUCT_ID,
                                IAPClientPaymentManager.FORCED_AUTOMATIC_RESTORATION);

                        if (errorCode != IAPClientPaymentManager.SUCCESS) {
                            // Show the error code
                            _infoText = "IAPClientPaymentManager:: purchaseProduct() failed with error code "
                                    + errorCode;
                            setStep(SHOW_RESULT);
                        }

                        break;

                    case SHOW_RESULT:
                        if(isBought()){
                            _midlet.launchApp();
                            isHidden = true;
                        }else{
                            setStep(START);
                        }
                }
                break;

            case Buttons.NEGATIVE_ACTION:
                switch (_currentStep) {
                    case START:
                        int errorCode = _iapClientPaymentManager.getProductData(PRODUCT_ID);

                        if (errorCode == IAPClientPaymentManager.SUCCESS) {
                            setStep(RETRIEVING_PRODUCT_INFORMATION);
                        } else {
                            // Show the error code
                            _infoText = "IAPClientPaymentManager:: getProductData() failed with error code "
                                    + errorCode;
                            setStep(SHOW_RESULT);
                        }

                        break;
                    default:
                        setStep(START);
                        break;
                }
                break;

            case Buttons.EXIT:
                _midlet.exit();
                break;
        }
    }

    /**
     * From ProgressBar.Listener.
     */
    public void onTimeout() {
        _infoText = "Failed to retrieve product information. Please check your network connection.";
        setStep(SHOW_RESULT);
    }

    /**
     * Helper method for setting the current step. Updates the buttons and the
     * step indicator based on the given step, and finally repaints the canvas.
     *
     * @param step The current step to set.
     */
    private void setStep(final int step) {
        _currentStep = step;
        _progressBar.setVisible(false);

        setupKeypad();

        // Set the info text and the buttons
        switch (step) {
            case START: {
                _infoText = "You have <" + triesLeft + "> tries left";
                if (_buttons != null) {
                    _buttons.setButtons("Try", (triesLeft > 0), "Buy", true);
                }
                break;
            }
            case RETRIEVING_PRODUCT_INFORMATION: {
                if (_buttons != null) {
                    _buttons.setButtons(null, false, "Cancel", true);
                }
                _infoText = "Retrieving product information. Please wait...";
                _progressBar.setVisible(true);
                _progressBar.start();
                break;
            }
            case SHOW_PRODUCT_INFORMATION: {
                if (_buttons != null) {
                    _buttons.setButtons("Buy", true, "Cancel", true);
                }
                break;
            }
            case SHOW_RESULT: {
                if (_buttons != null) {
                    _buttons.setButtons("Done", true, null, false);
                }
                break;
            }
            default: {
                System.out.println("Invalid step!");
                setStep(START);
                return;
            }
        }

        // Update the step indicator
        _stepIndicator.setCurrentStep(step);

        repaint();
    }
    ////////////////// keypad support below ///////
    ///////////////////////////////////////////////
    // right softkeys: negative commands STOP, CANCEL, BACK, EXIT
    static final Command exitCommand = new Command("Exit", Command.EXIT, 0);
    static final Command rightCancelCommand = new Command("Cancel", Command.CANCEL, 0);
    //middle softkeys: OK, ITEM 
    static final Command startCommand = new Command("Start", Command.OK, 0);
    static final Command buyCommand = new Command("Buy", Command.ITEM, 0);
    static final Command doneCommand = new Command("Done", Command.OK, 0);
    // left softkey: single positive command OK, ITEM, SCREEN or HELP 
    //               note OK, ITEM commands are reserved for middle softkey
    static final Command leftCancelCommand = new Command("Cancel", Command.SCREEN, 0);
    //

    private class State {

        public State(int id, Command leftSoftkey,
                Command midSoftkey, Command rigthSoftkey) {
            super();
            this.id = id;
            this.leftSoftkey = leftSoftkey;
            this.midSoftkey = midSoftkey;
            this.rigthSoftkey = rigthSoftkey;
        }
        public int id;
        public Command leftSoftkey;
        public Command midSoftkey;
        public Command rigthSoftkey;
    }
    private static State[] states;

    private void setupKeypad() {
        if (!_isKeypadON) {
            return;
        }

        for (int i = 0; i < states.length; i++) {
            State state = states[i];
            if (state.leftSoftkey != null) {
                removeCommand(state.leftSoftkey);
            }
            if (state.rigthSoftkey != null) {
                removeCommand(state.rigthSoftkey);
            }
            if (state.midSoftkey != null) {
                removeCommand(state.midSoftkey);
            }
        }

        for (int i = 0; i < states.length; i++) {
            State state = states[i];
            if (state.id == _currentStep) {
                if (state.leftSoftkey != null) {
                    addCommand(state.leftSoftkey);
                }
                if (state.rigthSoftkey != null) {
                    addCommand(state.rigthSoftkey);
                }
                if (state.midSoftkey != null) {
                    addCommand(state.midSoftkey);
                }
            }
        }

    }

    public void commandAction(Command cmd, Displayable displayable) {
        if (cmd.equals(exitCommand)) {
            _midlet.exit();
            return;
        }

        int errorCode = 0;
        switch (_currentStep) {
            case START:
                errorCode = _iapClientPaymentManager.getProductData(PRODUCT_ID);

                if (errorCode == IAPClientPaymentManager.SUCCESS) {
                    setStep(RETRIEVING_PRODUCT_INFORMATION);
                } else {
                    // Show the error code
                    _infoText = "IAPClientPaymentManager:: getProductData() failed with error code "
                            + errorCode;
                    setStep(SHOW_RESULT);
                }

                break;
            case RETRIEVING_PRODUCT_INFORMATION:
                errorCode = _iapClientPaymentManager.purchaseProduct(PRODUCT_ID,
                        IAPClientPaymentManager.FORCED_AUTOMATIC_RESTORATION);

                if (errorCode != IAPClientPaymentManager.SUCCESS) {
                    // Show the error code
                    _infoText = "IAPClientPaymentManager:: purchaseProduct() failed with error code "
                            + errorCode;
                    setStep(SHOW_RESULT);
                }
                break;
            case SHOW_PRODUCT_INFORMATION:
                if (cmd.equals(leftCancelCommand)) {
                    setStep(START);
                } else {
                    errorCode = _iapClientPaymentManager.purchaseProduct(PRODUCT_ID,
                            IAPClientPaymentManager.FORCED_AUTOMATIC_RESTORATION);

                    if (errorCode != IAPClientPaymentManager.SUCCESS) {
                        // Show the error code
                        _infoText = "IAPClientPaymentManager:: purchaseProduct() failed with error code "
                                + errorCode;
                        setStep(SHOW_RESULT);
                    }
                }
                break;
            case SHOW_RESULT:
                setStep(START);
                break;
            default:
                break;
        }
    }
/////////////////////////////////////// IAP specific is below //////////////////////

    public boolean isBought() {
        return (accessProductLiveMode() && accessProductTestMode());
    }

    private boolean accessProductLiveMode() {
        boolean result = false;
        try {
            InputStream input = _iapClientPaymentManager.getDRMResourceAsStream(DRM_PATH
                    + PRODUCT_ID + "/" + lockedFname);

            int data = input.read();
            StringBuffer screenBuf = new StringBuffer();
            while (data != -1) {
                screenBuf.append((char) data);
                data = input.read();
            }
            input.close();
            result = true;
        } catch (IOException e) {
        }

        return result;
    }

    private boolean accessProductTestMode() {
        boolean result = false;
        rms.open();
        Vector productArray = rms.get();
        if (!productArray.isEmpty()) {
            result = productArray.contains(PRODUCT_ID);
        }
        rms.close();
        return result;
    }

    private void serialiseProduct(String productId) {
        rms.open();
        rms.set(productId.getBytes());
        rms.close();
    }
    
    private int getProductTNBStatus(IAPClientUserAndDeviceData data) {
        int result = 0;
        InputStream istream = null;
        try {
            HttpConnection connection = null;

            String url = "http://trynbuyiap.appspot.com/mobile?appkey=XCP9L0Y4IASHKNGQ3UF1";
            connection = (HttpConnection) Connector.open(url, Connector.READ_WRITE, true);
            connection.setRequestMethod(HttpConnection.POST);
            connection.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");
            connection.setRequestProperty("Accept_Language", "en-US");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            String msg = data.getImei();
            OutputStream out = connection.openOutputStream();
            out.write(msg.getBytes());
            out.flush();
            
            if (connection.getResponseCode() == HttpConnection.HTTP_OK) {
                istream = connection.openInputStream();
                String buff;
                int length = (int) connection.getLength();
                if (length != -1) {
                    byte incomingData[] = new byte[length];
                    istream.read(incomingData);
                    buff = new String(incomingData);
                } else {
                    ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
                    int ch;
                    while ((ch = istream.read()) != -1) {
                        bytestream.write(ch);
                    }
                    buff = new String(bytestream.toByteArray());
                    bytestream.close();
                }
                JSONObject json = new JSONObject(buff) ;
                result = json.getInt("left");
            }
        } catch (IOException ex) {
            _midlet.exit();
//        } catch (SecurityException ex){ //security exception is not suppressed because it does not happen in signed midlet
//            _midlet.exit();
        } catch (JSONException ex) {
              ex.printStackTrace();
        }finally{
            try {
                if(istream != null)
                    istream.close();
            } catch (IOException ex) {}
        }
        return result;
    }
            
}
